package values

import (
	"bytes"
	"reflect"
	"unicode/utf8"
	"unsafe"

	"golang.org/x/text/width"
)

// UnderefVal 探测 any 目标, 将指针类型脱敏为原始标量类型
//   - 如果目标是nil, 则返回nil
//   - 如果目标非指针, 则返回原值
//   - 如果目标含有方法, 则返回原值
//   - 如果目标是nil指针, 则返回nil
//   - 如果目标是非nil指针, 则返回指针指向的指向的值(any类型的原始标量)
func UnderefVal(v any) any {
	if v == nil {
		return nil
	}
	val := reflect.ValueOf(v)
	if !val.IsValid() || val.Kind() != reflect.Ptr {
		return v
	}
	if val.NumMethod() > 0 {
		return v
	}
	// 脱敏指针类型, 直到非指针为止
	seen := make(map[uintptr]struct{})
	for val.Kind() == reflect.Ptr {
		if val.IsNil() {
			return nil
		}
		// 防止循环引用
		if val.CanAddr() {
			addr := val.UnsafeAddr()
			if _, exists := seen[addr]; exists {
				return nil
			}
			seen[addr] = struct{}{}
		}
		val = val.Elem()
	}
	switch val.Kind() {
	// reflect.Pointer, // Pointer == Ptr
	case reflect.Chan, reflect.Func, reflect.Map, reflect.UnsafePointer,
		reflect.Interface, reflect.Slice:
		if val.IsNil() {
			return nil
		}
	}
	if !val.IsValid() || !val.CanInterface() {
		return nil
	}
	return val.Interface()
}

// StringToBytes 字符串转Bytes切片
//   - 在go版本<1.20中, 通过unsafe包和reflect包实现，其主要原理是拿到底层数组的指针，然后转换成[]byte或string。
//   - 在go版本>=1.20中 由于安全性问题reflect包中的StringHeader和SliceHeader已被标注为deprecated，建议使用unsafe包来实现转换。
//   - Use unsafe.String or unsafe.StringData instead.
//   - Use unsafe.Slice or unsafe.SliceData instead.
func StringToBytes(data string) []byte {
	size := len(data)
	if size == 0 {
		return []byte{}
	}
	return unsafe.Slice(unsafe.StringData(data), size)
}

// BytesToString Bytes切片转字符串
func BytesToString(data []byte) string {
	size := len(data)
	if size == 0 {
		return ""
	}
	return unsafe.String(unsafe.SliceData(data), size)
}

// StringToRunes 字符串转rune切片
func StringToRunes(str string) []rune {
	size := utf8.RuneCountInString(str)
	if size == 0 {
		return []rune{}
	}
	// 按字符rune遍历字符串
	var buf = make([]rune, 0, size)
	for _, r := range str {
		buf = append(buf, r)
	}
	return buf
}

// RunesToString Runes切片转字符串
func RunesToString(data []rune) string {
	size := len(data)
	if size == 0 {
		return ""
	}
	var buf bytes.Buffer
	for i := range size {
		buf.WriteRune(data[i])
	}
	return buf.String()
}

// RunesToBytes Runes切片转Bytes切片
func RunesToBytes(data []rune) []byte {
	size := len(data)
	if size == 0 {
		return []byte{}
	}
	var buf bytes.Buffer
	for i := range size {
		buf.WriteRune(data[i])
	}
	return buf.Bytes()
}

// BytesToRunes Bytes切片转Runes切片
func BytesToRunes(data []byte) []rune {
	size := utf8.RuneCount(data)
	if size == 0 {
		return []rune{}
	}
	var buf = make([]rune, 0, size)
	for len(data) > 0 {
		r, size := utf8.DecodeRune(data)
		buf = append(buf, r)
		data = data[size:]
	}
	return buf
}

// StringToSBC 全角字符串转半角
func StringToSBC(s string) string {
	return width.Narrow.String(s)
}

// BytesToSBC 全角字节转半角字节
func BytesToSBC(s []byte) []byte {
	return width.Narrow.Bytes(s)
}

// StringToDBC 半角字符串转全角字符串
func StringToDBC(s string) string {
	return width.Widen.String(s)
}

// BytesToDBC 半角字节转全角字节
func BytesToDBC(s []byte) []byte {
	return width.Widen.Bytes(s)
}
